package com.googlecode.afdbaccess.web.common.login;

import javax.security.auth.login.AccountNotFoundException;
import javax.security.auth.login.LoginException;

import org.apache.log4j.Logger;

import com.googlecode.afdbaccess.cache.DbCache;
import com.googlecode.afdbaccess.cache.TransactionAutoCommit;
import com.googlecode.afdbaccess.cache.TransactionBase;
import com.googlecode.afdbaccess.cache.resultconversion.exceptions.DataLayerWrappedResultSetConversionException;
import com.googlecode.afdbaccess.cache.resultconversion.exceptions.NoRowsReturnedException;

abstract class TransactedTask<T> {
    private final Logger log = Logger.getLogger(getClass());

    abstract protected T perform(TransactionBase t, String userName) throws Exception;
    
    protected TransactionAutoCommit getNewTransaction(DbCache dbCache, String source) {
        return new TransactionAutoCommit(dbCache, "login-module, " + source);
    }
    

    T performWithErrorHandlingAndCleanup(DbCache dbCache, String source, String userName) throws LoginException {
        log.debug(source + " userName: " + userName);

        TransactionAutoCommit t = getNewTransaction(dbCache, source);
        try {
            try {
                return perform(t, userName);
            } catch (DataLayerWrappedResultSetConversionException exception) {
                if(exception.getCause() instanceof NoRowsReturnedException) {
                    AccountNotFoundException accountNotFoundException = new AccountNotFoundException("Account could not be found in database.");
                    accountNotFoundException.initCause(exception);
                    throw accountNotFoundException;
                } else {
                  throw exception;
                }
            }
        } catch (Exception exception) {
            throw logAndCastToLoginException(exception, "Failed to fetch.");
        } finally {
            t.close();
        }
    }

    private LoginException logAndCastToLoginException(Exception throwable, String message) {
        log.debug("Got an exception. Logging it and rethrowing it, since the higher layers don't seem to log it ever.", throwable);
        
        if(throwable instanceof LoginException) {
            return (LoginException)throwable;
        } else {
            LoginException loginException = new LoginException(message);
            loginException.initCause(throwable);
            return loginException;
        }
    }
}
